Skip to content

fix(cli): guard customDeepMerge against circular references#28387

Open
vedhakoushik wants to merge 1 commit into
google-gemini:mainfrom
vedhakoushik:fix/deepmerge-circular-refs-v2
Open

fix(cli): guard customDeepMerge against circular references#28387
vedhakoushik wants to merge 1 commit into
google-gemini:mainfrom
vedhakoushik:fix/deepmerge-circular-refs-v2

Conversation

@vedhakoushik

Copy link
Copy Markdown

What

Fixes #28270. customDeepMerge's mergeRecursively recursed into nested plain objects with no cycle tracking, so a settings object containing a circular reference (e.g. obj.self = obj) caused unbounded recursion and crashed the settings manager with RangeError: Maximum call stack size exceeded, breaking CLI initialization until the user manually scrubbed their config.

The repo already documented this limitation with a test in settings.test.ts that asserted the crash (.toThrow(/Maximum call stack size exceeded/)).

Fix

packages/cli/src/utils/deepMerge.ts — track the chain of source-to-clone mappings currently being merged in a Map, added on entry and removed on exit so it represents only the current ancestor path (not already-processed siblings). When a source value is a circular reference back to an ancestor, assign the ancestor's clone rather than recursing, so the merged result is a fully independent clone (result.self === result) and never embeds a live reference back into the caller's input.

This is deliberately scoped so that shared-but-non-circular references (DAGs) still merge normally — only true cycles are short-circuited.

Tests

  • deepMerge.test.ts: added regression tests for self-reference (obj.self = obj), mutual/indirect cycles (a → b → a), and a shared non-circular reference (DAG) to guard against over-eager short-circuiting.
  • settings.test.ts: flipped the existing circular-reference test from asserting the crash to asserting it no longer throws.

Verification

  • deepMerge.test.ts — 20/20 pass (4 new)
  • settings.test.ts circular-reference test — passes
  • eslint, tsc --noEmit, and prettier --check all clean on the changed files

Supersedes #28349, opened as a clean re-submission — the original PR's commits carried a Co-Authored-By trailer that blocked the CLA check from resolving. Same fix, same tests, no code changes; only the commit metadata differs.

customDeepMerge's mergeRecursively recursed into nested plain objects with
no cycle tracking, so a settings object containing a circular reference
(e.g. obj.self = obj) caused unbounded recursion and crashed the settings
manager with RangeError: Maximum call stack size exceeded, breaking CLI
initialization until the user manually scrubbed their config. The repo
already documented this with a test asserting the crash.

Track the chain of source-to-clone mappings currently being merged in a
Map. When a source value is a circular reference back to an ancestor,
assign the ancestor's clone rather than recursing, so the merged result
is a fully independent clone (result.self === result) and never embeds a
live reference back into the caller's input. Shared-but-non-circular
references (DAGs) are unaffected and still merge normally.

Add regression tests for self, mutual (a->b->a), and shared-reference
cases, and flip the settings.test.ts circular-reference test from
asserting the crash to asserting it no longer throws.

Fixes google-gemini#28270

Signed-off-by: Bellam vedha kousik <bellam.vedhakoushik@gmail.com>
@vedhakoushik vedhakoushik requested a review from a team as a code owner July 13, 2026 07:43
@github-actions github-actions Bot added the size/m A medium sized PR label Jul 13, 2026
@github-actions

Copy link
Copy Markdown

📊 PR Size: size/M

  • Lines changed: 85
  • Additions: +79
  • Deletions: -6
  • Files changed: 3

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses a critical issue where the customDeepMerge utility would crash with a stack overflow when encountering circular references in configuration objects. By introducing a tracking mechanism during the recursive merge process, the fix safely identifies cycles and maps them to the corresponding clone, allowing the merge to complete successfully without unbounded recursion.

Highlights

  • Circular Reference Handling: Implemented cycle tracking in customDeepMerge using a Map to detect and short-circuit circular references, preventing stack overflow errors.
  • Independent Cloning: Ensured that circular references are correctly reproduced within the cloned structure, maintaining independence from the original source object.
  • Regression Testing: Added comprehensive tests for self-referential objects, mutual cycles, and shared non-circular references to ensure robustness.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the customDeepMerge utility to handle circular references (both self-referential and mutual) without throwing a stack overflow error. It achieves this by tracking the chain of source objects being merged using a clones Map, resolving circular references to their respective clones, and cleaning up the map on recursion exit to allow normal merging of shared but non-circular references. Corresponding unit tests have been added and updated to verify this behavior. There are no review comments to address.

@gemini-cli gemini-cli Bot added priority/p2 Important but can be addressed in a future release. area/core Issues related to User Interface, OS Support, Core Functionality labels Jul 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/core Issues related to User Interface, OS Support, Core Functionality priority/p2 Important but can be addressed in a future release. size/m A medium sized PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant